package sf.database.mapper;

import com.querydsl.sql.AbstractSQLQuery;
import com.querydsl.sql.SQLQuery;
import org.jooq.Select;
import sf.common.wrapper.Page;
import sf.core.DBObject;
import sf.database.dao.DBClient;
import sf.database.dao.DBContext;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.OrmIterator;
import sf.database.jdbc.sql.OrmStream;
import sf.database.meta.MetaHolder;
import sf.database.meta.TableMapping;
import sf.jooq.JooqTables;
import sf.jooq.tables.JooqTable;
import sf.querydsl.QueryDSLTables;
import sf.querydsl.SQLRelationalPath;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * daoMapper实现类
 * @Author: sxf
 * @Date: 2018/3/25 11:04
 */
public class DaoMapperImpl<T extends DBObject> implements DaoMapper<T> {
    protected DBClient dbClient;
    protected Class<T> domainClass;

    public DaoMapperImpl(Class<T> domainClass, DBClient dbClient) {
        this.domainClass = domainClass;
        this.dbClient = dbClient;
    }

    @Override
    public SQLRelationalPath<T> queryDSLTable() {
        return QueryDSLTables.relationalPathBase(null, domainClass);
    }

    @Override
    public SQLRelationalPath<T> queryDSLTable(DBContext context) {
        return QueryDSLTables.relationalPathBase(context, domainClass);
    }

    @Override
    public JooqTable<?> jooqTable() {
        DBDialect dialect = dbClient.getDialect(false);
        return JooqTables.getTable(null,domainClass, dialect);
    }

    @Override
    public JooqTable<?> jooqTable(DBContext context) {
        DBDialect dialect = dbClient.getDialect(false);
        return JooqTables.getTable(context,domainClass, dialect);
    }

    @Override
    public int merge(T entity) {
        return getDbClient().merge(entity);
    }

    @Override
    public int insert(T t) {
        return getDbClient().insert(t);
    }

    @Override
    public int[] insertBatch(List<T> list) {
        return getDbClient().batchInsert(list);
    }

    @Override
    public int[] insertBatchFast(List<T> list) {
        return getDbClient().batchInsertFast(list);
    }

    @Override
    public int updateById(T entity) {
        return getDbClient().update(entity);
    }

    @Override
    public int update(T entity) {
        return getDbClient().update(entity);
    }

    @Override
    public int[] updateBatch(List<T> entity) {
        return getDbClient().batchUpdate(entity);
    }

    @Override
    public int delete(T entity) {
        return getDbClient().delete(entity);
    }

    @Override
    public int deleteById(Object... key) {
        return getDbClient().deleteByPrimaryKeys(domainClass, key);
    }

    @Override
    public int[] deleteInBatch(Iterable<T> entities) {
        return getDbClient().batchDelete(asList(entities));
    }

    @Override
    public void deleteAllInBatch() {
        DBClient db = getDbClient();
        db.truncate(domainClass);
    }

    ///////////////////////////////分割线，写方法在上，读方法在下///////////////////////////////////

    @Override
    public T unique(Object... keys) {
        return getDbClient().selectByPrimaryKeys(domainClass, keys);
    }

    @Override
    public T single(T query) {
        return getDbClient().selectOne(query);
    }

    @Override
    public T lock(T query) {
        return getDbClient().selectOneForUpdate(query);
    }

    @Override
    public long count() {
        DBDialect dialect = dbClient.getDialect(false);
        String sql = getSelectCountSql(dialect, domainClass);
        Long count = dbClient.selectOne(Long.class, sql);
        if (count == null) {
            count = 0L;
        }
        return count;
    }

    @Override
    public long count(T query) {
        return dbClient.selectCount(query);
    }

    private <T extends DBObject> String getSelectCountSql(DBDialect dialect, Class<T> clz) {
        StringBuilder sql = new StringBuilder();
        TableMapping tm = MetaHolder.getMeta(clz);
        sql.append("select count(1) from ").append(dialect.wrapKeyword(tm.getTableName()));
        return sql.toString();
    }

    @Override
    public boolean existsById(Object... id) {
        return dbClient.existsByPrimaryKeys(domainClass, id);
    }

    @Override
    public boolean exists(T query) {
        return dbClient.exists(query);
    }

    @Override
    public List<T> selectList(T query) {
        return getDbClient().selectList(query);
    }

    @Override
    public Page<T> selectPage(T query, int start, int size) {
        return getDbClient().selectPage(query, start, size);
    }

    //////////////// queryDSL ///////////////
    @Override
    public List<T> queryDSLSelectList(AbstractSQLQuery<T, SQLQuery<T>> query) {
        return getDbClient().queryDSLSelectList(query, domainClass);
    }

    @Override
    public Page<T> queryDSLSelectPage(AbstractSQLQuery<T, SQLQuery<T>> query, int start, int limit) {
        return getDbClient().queryDSLSelectPage(query, domainClass, start, limit);
    }

    @Override
    public <S> S queryDSLSelectOne(AbstractSQLQuery<T, SQLQuery<T>> query, Class<S> clz) {
        return getDbClient().queryDSLSelectOne(query, clz);
    }

    @Override
    public T queryDSLSelectOne(AbstractSQLQuery<T, SQLQuery<T>> query) {
        return getDbClient().queryDSLSelectOne(query, domainClass);
    }

    @Override
    public void queryDSLSelectIterator(OrmIterator<T> ormIt, AbstractSQLQuery<T, SQLQuery<T>> query) {
        getDbClient().queryDSLSelectIterator(ormIt, domainClass, query);
    }

    @Override
    public void queryDSLSelectStream(OrmStream<T> ormStream, AbstractSQLQuery<T, SQLQuery<T>> query) {
        getDbClient().queryDSLSelectStream(ormStream, domainClass, query);
    }

    //////////////// jooq ///////////////
    @Override
    public T jooqSelectOne(Select<?> select) {
        return getDbClient().jooqSelectOne(select, domainClass);
    }

    @Override
    public <S> S jooqSelectOne(Select<?> select, Class<S> clz) {
        return getDbClient().jooqSelectOne(select, clz);
    }

    @Override
    public List<T> jooqSelectList(Select<?> select) {
        return getDbClient().jooqSelectList(select, domainClass);
    }

    @Override
    public Page<T> jooqSelectPage(Select<?> countSelect, Select<?> pageSelect) {
        return getDbClient().jooqSelectPage(countSelect, pageSelect, domainClass);
    }

    @Override
    public void jooqSelectIterator(OrmIterator<T> ormIt, Select<?> select) {
        getDbClient().jooqSelectIterator(ormIt, domainClass, select);
    }

    @Override
    public void jooqSelectStream(OrmStream<T> ormStream, Select<?> select) {
        getDbClient().jooqSelectStream(ormStream, domainClass, select);
    }

    //////////////// 模板sql ///////////////
    @Override
    public List<T> selectTemplate(String sql, Map<String, Object> paras) {
        return getDbClient().selectListTemplateSource(domainClass, sql, paras);
    }

    @Override
    public int executeTemplate(String sql, Map<String, Object> paras) {
        return getDbClient().executeTemplateSource(sql, paras);
    }

    // //////////////////////// 分割线 ///////////////////////////

    private <S> List<S> asList(Iterable<S> entities) {
        List<S> list = new ArrayList<S>();
        for (S entity : entities) {
            list.add(entity);
        }
        return list;
    }

    @Override
    public DBClient getDbClient() {
        return dbClient;
    }

    @Override
    public Class<T> getDomainClass() {
        return domainClass;
    }
}
